package io.kiki.sba.registry.server.data_store.multi.cluster.sync.info;


import com.google.common.collect.Sets;
import io.kiki.sba.registry.common.model.console.MultiSegmentSyncSwitch;
import io.kiki.sba.registry.common.model.metaserver.MultiClusterSyncInfo;
import io.kiki.sba.registry.common.model.slot.filter.MultiSyncDataAcceptorManager;
import io.kiki.sba.registry.server.data_store.bootstrap.MultiClusterDataServerConfig;
import io.kiki.sba.registry.store.api.meta.MultiClusterSyncRepository;
import io.kiki.sba.registry.util.ConcurrentUtils;
import io.kiki.sba.registry.util.WakeUpLoopExecuteTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;


public class FetchMultiSyncService implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger logger = LoggerFactory.getLogger(FetchMultiSyncService.class);
    private static final boolean INIT = false;
    private final Worker worker = new Worker();
    @Autowired
    private MultiClusterDataServerConfig multiClusterDataServerConfig;
    @Autowired
    private MultiClusterSyncRepository multiClusterSyncRepository;
    @Autowired
    private MultiSyncDataAcceptorManager multiSyncDataAcceptorManager;
    private Map<String, MultiSegmentSyncSwitch> syncMap = new HashMap<>();

    public synchronized boolean multiSync(String dataCenter) {
        MultiSegmentSyncSwitch multiSegmentSyncSwitch = syncMap.get(dataCenter);
        if (multiSegmentSyncSwitch == null) {
            return INIT;
        }

        return multiSegmentSyncSwitch.isMultiSync();
    }

    public synchronized boolean multiPush(String dataCenter) {
        MultiSegmentSyncSwitch multiSegmentSyncSwitch = syncMap.get(dataCenter);
        if (multiSegmentSyncSwitch == null) {
            return INIT;
        }

        return multiSegmentSyncSwitch.isMultiPush();
    }

    public synchronized MultiSegmentSyncSwitch getMultiSyncSwitch(String dataCenter) {
        return syncMap.get(dataCenter);
    }

    public synchronized void setSyncMap(Map<String, MultiSegmentSyncSwitch> syncMap) {
        this.syncMap = syncMap;
    }

    private synchronized Set<String> syncingDataCenter() {
        return syncMap.keySet();
    }


    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        start();
    }

    public void start() {
        ConcurrentUtils.createDaemonThread("FetchMultiSyncInfo-Worker", worker).start();
    }

    private MultiSegmentSyncSwitch from(MultiClusterSyncInfo multiClusterSyncInfo) {

        return new MultiSegmentSyncSwitch(multiClusterSyncInfo.isEnableSyncDatum(), multiClusterSyncInfo.isEnablePush(), multiClusterSyncInfo.getRemoteDataCenter(), multiClusterSyncInfo.getSynPublisherGroups(), multiClusterSyncInfo.getSyncDataInfoIds(), multiClusterSyncInfo.getIgnoreDataInfoIds(), multiClusterSyncInfo.getDataVersion());
    }

    private class Worker extends WakeUpLoopExecuteTask {

        @Override
        public void _execute_() {
            Set<MultiClusterSyncInfo> set = multiClusterSyncRepository.queryLocalMultiClusterSyncInfoSet();

            boolean change = false;
            Map<String, MultiSegmentSyncSwitch> map = new HashMap<>(set.size());
            for (MultiClusterSyncInfo multiClusterSyncInfo : set) {
                MultiSegmentSyncSwitch exist = getMultiSyncSwitch(multiClusterSyncInfo.getRemoteDataCenter());
                if (exist != null && exist.getDataVersion() > multiClusterSyncInfo.getDataVersion()) {
                    logger.error("[FetchMultiSyncService]load config error, exist:{}, load:{}", exist, multiClusterSyncInfo);
                    return;
                }

                if (exist == null || multiClusterSyncInfo.getDataVersion() > exist.getDataVersion()) {
                    logger.info("[FetchMultiSyncService.addOrUpdate]dataCenter:{},remoteDataCenter:{}, config update from:{} to {}", multiClusterSyncInfo.getDataCenter(), multiClusterSyncInfo.getRemoteDataCenter(), exist, multiClusterSyncInfo);
                    change = true;
                }
                map.put(multiClusterSyncInfo.getRemoteDataCenter(), from(multiClusterSyncInfo));
            }

            Set<String> remove = Sets.difference(syncingDataCenter(), map.keySet());
            if (!CollectionUtils.isEmpty(remove)) {
                change = true;
                logger.info("[FetchMultiSyncService.remove]remove dataCenters:{}", remove);
            }
            if (change) {
                multiSyncDataAcceptorManager.updateFrom(map.values());
                setSyncMap(map);
            }
        }

        @Override
        public int getWaitingMillis() {
            return multiClusterDataServerConfig.getMultiClusterConfigReloadMillis();
        }
    }
}
